﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Threading;
using System.Web;
using viBlog.Classes;
using viBlog.Providers;
using visinia.SmartEngine;
using visinia.SmartEngine.Cache;

namespace viBlog.Controllers
{
    /// <summary>
    /// The controller for the blog posts
    /// </summary>
    public class PostController
    {
        #region CACHE KEYS
        private const string KEY_POST = "post_{0}";
        private const string KEY_POST_DESCRIPTION = "postDescription_{0}";
        private const int POST_DESCRIPTION_EXPIRY = 20;
        private const string KEY_POST_RELATIVES = "postRelatives_{0}";
        private const int POST_RELATIVES_EXPIRY = 20;        
        #endregion

        #region ACCESS
        /// <summary>
        /// Returns a Specific Post
        /// </summary>
        /// <param name="Post">PostId of the Post to Access</param>
        /// <returns>The Post whose PostId is given</returns>
        public static Post GetPost(int PostId)
        {
            string key = string.Format(KEY_POST, PostId);
            Post post = (Post)DataCache.GetCache(key);
            if (post == null)
            {
                post = FillPost(viBlogDataProvider.Provider.GetPost(PostId));
                DataCache.SetCache(key, post);
            }
            return post;
        }

        /// <summary>
        /// Gets all the blog posts
        /// </summary>
        /// <returns></returns>
        public static List<Post> GetPosts()
        {
            return GetPosts(PostIdsAll(), PostIdsAll().Count);
        }

        /// <summary>
        /// Gets the specified number of the latest blog posts
        /// </summary>
        /// <param name="count"></param>
        /// <returns></returns>
        public static List<Post> GetPosts(int Count)
        {
            return GetPosts(PostIdsAll(), Count);
        }

        private static List<int> PostIdsAll()
        {
            string key = "postAll";
            List<int> postIds = (List<int>)DataCache.GetCache(key);
            if (postIds == null)
            {
                postIds = new List<int>();
                IDataReader dr = viBlogDataProvider.Provider.GetPosts();
                while (dr.Read())
                {
                    if (dr["PostId"] != Convert.DBNull)
                        postIds.Add((int)dr["PostId"]);
                }
                dr.Close();
                DataCache.SetCache(key, postIds, new TimeSpan(0, 20, 0));
            }
            return postIds;
        }

        /// <summary>
        /// Gets the specified number of specified category related post
        /// </summary>
        /// <param name="category"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static List<Post> GetPosts(Category category, int Count)
        {
            return GetPosts(PostIdsByCategory(category), Count);
        }

        private static List<int> PostIdsByCategory(Category category)
        {
            string key = string.Format("postByCategory_{0}", category.CategoryId);
            List<int> postIds = (List<int>)DataCache.GetCache(key);
            if (postIds == null)
            {
                postIds = new List<int>();
                IDataReader dr = viBlogDataProvider.Provider.GetPostsByCategory(category.CategoryId);
                while (dr.Read())
                {
                    if (dr["PostId"] != Convert.DBNull)
                        postIds.Add((int)dr["PostId"]);
                }
                dr.Close();
                DataCache.SetCache(key, postIds, new TimeSpan(0, 20, 0));
            }
            return postIds;
        }

        /// <summary>
        /// Gets the specified number of specified tag blog posts
        /// </summary>
        /// <param name="tag"></param>
        /// <param name="Count"></param>
        /// <returns></returns>
        public static List<Post> GetPosts(Tag tag, int Count)
        {
            return GetPosts(PostIdsByTag(tag), Count);
        }

        private static List<int> PostIdsByTag(Tag tag)
        {
            string key = string.Format("postByTagsCount_{0}", tag.TagId);
            List<int> postIds = (List<int>)DataCache.GetCache(key);
            if (postIds == null)
            {
                postIds = new List<int>();
                IDataReader dr = viBlogDataProvider.Provider.GetPostsByTag(tag.TagId);
                while (dr.Read())
                {
                    if (dr["PostId"] != Convert.DBNull)
                        postIds.Add((int)dr["PostId"]);
                }
                dr.Close();
                DataCache.SetCache(key, postIds, new TimeSpan(0, 20, 0));
            }
            return postIds;
        }

        /// <summary>
        /// Gets the specified number of the posts listed in the PostsList
        /// </summary>
        /// <param name="PostsList"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static List<Post> GetPosts(string PostsList, int Count)
        {
            if (string.IsNullOrEmpty(PostsList))
                return null;
            string key = string.Format("postByList_{0}", PostsList);
            List<int> postIds = (List<int>)DataCache.GetCache(key);
            if (postIds == null)
            {
                postIds = new List<int>();
                int temp = 0;
                foreach (string postid in PostsList.Split(','))
                {
                    if (Int32.TryParse(postid, out temp))
                        postIds.Add(temp);
                }
                DataCache.SetCache(key, postIds, new TimeSpan(0, 20, 0));
            }
            return GetPosts(postIds, Count);
        }

        /// <summary>
        /// Gets all the posts between the given time frame
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        public static List<Post> GetPosts(DateTime date)
        {
            return GetPosts(PostIdsByDate(date), PostIdsByDate(date).Count);
        }

        private static List<int> PostIdsByDate(DateTime date)
        {
            string key = string.Format("postByDate_{0}", date);
            List<int> postIds = (List<int>)DataCache.GetCache(key);
            if (postIds == null)
            {
                postIds = new List<int>();
                IDataReader dr = viBlogDataProvider.Provider.GetPostsByDate(date.Year, date.Month);
                while (dr.Read())
                {
                    if (dr["PostId"] != Convert.DBNull)
                        postIds.Add((int)dr["PostId"]);
                }
                dr.Close();
                DataCache.SetCache(key, postIds, new TimeSpan(0, 20, 0));
            }
            return postIds;
        }

        public static int GetPostsCount(DateTime date)
        {
            return PostIdsByDate(date).Count;
        }

        public static int GetPostsCount()
        {
            return PostIdsAll().Count;
        }

        public static int GetPostsCount(Category category)
        {
            return PostIdsByCategory(category).Count;
        }

        public static int GetPostsCount(Tag tag)
        {
            return PostIdsByTag(tag).Count;
        }

        internal static List<Post> GetPosts(List<int> PostIds, int Count)
        {
            List<Post> posts = new List<Post>();
            foreach (int PostId in PostIds)
            {
                if (Count-- == 0)
                    break;
                posts.Add(GetPost(PostId));
            }
            return posts;
        }

        /// <summary>
        /// Gets the description of the specified post
        /// </summary>
        /// <param name="PostId">The post id whose description to return</param>
        /// <returns>Returns the description</returns>
        public static string GetPostDescription(int PostId)
        {
            string key = string.Format(KEY_POST_DESCRIPTION, PostId);
            string postDescription = (string)DataCache.GetCache(key);
            if (postDescription == null)
            {
                IDataReader dr = viBlogDataProvider.Provider.GetPostDescription(PostId);
                if (dr.Read())
                {
                    if (!dr.IsDBNull(0))
                        postDescription = dr.GetString(0);
                    // resolve image urls
                    postDescription = ImageController.ResolveImagesForDisplay(postDescription);
                    DataCache.SetCache(key, postDescription, new TimeSpan(0, POST_DESCRIPTION_EXPIRY, 0));
                }
                dr.Close();
            }
            return postDescription;
        }

        /// <summary>
        /// Gets all the blog posts including drafts but for only admin role members
        /// </summary>
        /// <returns></returns>
        public static List<Post> GetPostsAdmin()
        {
            if (Thread.CurrentPrincipal.IsInRole("admin"))
            {
                string key = "postsAllAdmin";
                List<Post> list = (List<Post>)DataCache.GetCache(key);
                if (list == null)
                {
                    list = FillPosts(viBlogDataProvider.Provider.GetPostsAdmin());
                    DataCache.SetCache(key, list, new TimeSpan(0, 20, 0));
                }
                return list;
            }
            return null;
        }

        /// <summary>
        /// Returns a Specific Post including draft but for only admin role members
        /// </summary>
        /// <param name="Post">PostId of the Post to Access</param>
        /// <returns>The Post whose PostId is given</returns>
        public static Post GetPostAdmin(int PostId)
        {
            return GetPostsAdmin().Find(p => p.PostId == PostId);
        }

        /// <summary>
        /// Returns drafts but for only admin role members
        /// </summary>        
        public static List<Post> GetDraftsAdmin()
        {
            return GetPostsAdmin().FindAll(p => p.IsDraft == true);
        }

        /// <summary>
        /// Gets the description of the specified post
        /// </summary>
        /// <param name="PostId">The post id whose description to return</param>
        /// <returns>Returns the description</returns>
        public static string GetPostDescriptionAdmin(int PostId)
        {
            if (Thread.CurrentPrincipal.IsInRole("admin"))
            {
                string key = string.Format("postDescriptionAdmin_{0}", PostId);
                string postDescription = (string)DataCache.GetCache(key);
                if (postDescription == null)
                {
                    IDataReader dr = viBlogDataProvider.Provider.GetPostDescriptionAdmin(PostId);
                    if (dr.Read())
                    {
                        if (!dr.IsDBNull(0))
                            postDescription = dr.GetString(0);
                        // resolve image urls
                        postDescription = ImageController.ResolveImagesForDisplay(postDescription);
                        DataCache.SetCache(key, postDescription, new TimeSpan(0, POST_DESCRIPTION_EXPIRY, 0));
                    }
                    dr.Close();
                }
                return postDescription;
            }
            return null;
        }

        /// <summary>
        /// Gets the post ids of the previous and next posts of the specified post and also the first and last posts of the blog
        /// </summary>
        /// <param name="PostId">The post id whose description to return</param>
        /// <returns>Returns the description</returns>
        public static PostRelatives GetPostRelatives(int PostId)
        {
            string key = string.Format(KEY_POST_RELATIVES, PostId);
            PostRelatives postRelatives = (PostRelatives)DataCache.GetCache(key);
            if (postRelatives == null)
            {
                postRelatives = new PostRelatives();
                IDataReader dr = viBlogDataProvider.Provider.GetPostRelatives(PostId);
                if (dr.Read())
                {
                    if (!dr.IsDBNull(0))
                        postRelatives.PreviousPostId = dr.GetInt32(0);
                    if (!dr.IsDBNull(1))
                        postRelatives.NextPostId = dr.GetInt32(1);
                    if (!dr.IsDBNull(2))
                        postRelatives.FirstPostId = dr.GetInt32(2);
                    if (!dr.IsDBNull(3))
                        postRelatives.LastPostId = dr.GetInt32(3);
                    DataCache.SetCache(key, postRelatives, new TimeSpan(0, POST_RELATIVES_EXPIRY, 0));
                }
                dr.Close();
            }
            return postRelatives;
        }

        /// <summary>
        /// Gets the blog posts archive data
        /// </summary>
        /// <returns></returns>
        public static List<ArchiveData> GetArchives()
        {
            List<ArchiveData> list = (List<ArchiveData>)DataCache.GetCache("postArchives");
            if (list == null)
            {
                list = FillArchives(viBlogDataProvider.Provider.GetArchives());
                DataCache.SetCache("postArchives", list);
            }
            return list;
        }

        /// <summary>
        /// Generates the url for a post
        /// </summary>        
        private static string GenerateUrl(Post post)
        {   /*****************************************************************************************/
            // Post URL = Blog/Post/PostId/some.thing.imp.aspx
            /*****************************************************************************************/

            return BlogController.BlogUrlSeparator + "/" + "Post" + "/" + post.PostId.ToString() + "/" + post.Url + ".aspx";
        }
        #endregion

        #region DATA
        /// <summary>
        /// Adds a new Post & Saves it to DataStore
        /// </summary>
        /// <param name="Post">The Post to be added </param>
        /// <param name="description">The post contents</param>
        public static int AddPost(Post post, string description)
        {
            IDataReader dr = viBlogDataProvider.Provider.AddPost(post.Title, post.Author, post.Date, post.Summary, post.Url, post.AllowComments, post.IsDraft, description);
            int index = 0;
            while (dr.Read())
            {
                if (dr[0] != Convert.DBNull)
                    index = (int)dr[0];
            }
            dr.Close();
            DataCache.DeleteCacheMultiple("post");
            return index;
        }

        /// <summary>
        /// Updates the Post
        /// </summary>
        /// <param name="Post"></param>
        /// <param name="description">The post contents</param>
        public static void UpdatePost(Post post, string description)
        {
            post.Title = HttpUtility.HtmlDecode(post.Title);
            viBlogDataProvider.Provider.UpdatePost(post.PostId, post.Title, post.Author, post.Date, post.Summary, post.Url, post.AllowComments, post.IsDraft, description);
            DataCache.DeleteCacheMultiple("post");
        }

        /// <summary>
        /// Deletes the Post
        /// </summary>
        /// <param name="PostId">Id of the Post to Delete</param>
        public static void DeletePost(int PostId)
        {
            viBlogDataProvider.Provider.DeletePost(PostId);
            DataCache.DeleteCacheMultiple("post");
        }
        #endregion

        #region HELPERS
        private static Post FillPost(IDataReader dr)
        {
            while (dr.Read())
            {
                return ActualRead(dr, true);
            }
            return null;
        }
        private static List<Post> FillPosts(IDataReader dr)
        {
            List<Post> list = new List<Post>();
            while (dr.Read())
            {
                list.Add(ActualRead(dr, false));
            }
            dr.Close();
            return list;
        }

        private static Post ActualRead(IDataReader dr, bool CloseDataReader)
        {
            Post post = new Post();
            if (dr["PostId"] != Convert.DBNull)
                post.PostId = (int)dr["PostId"];
            if (dr["Title"] != Convert.DBNull)
                post.Title = HttpUtility.HtmlEncode((string)dr["Title"]);
            if (dr["Author"] != Convert.DBNull)
                post.Author = (string)dr["Author"];
            if (dr["Date"] != Convert.DBNull)
                post.Date = (DateTime)dr["Date"];
            if (dr["Summary"] != Convert.DBNull)
                post.Summary = (string)dr["Summary"];
            if (dr["Url"] != Convert.DBNull)
                post.Url = (string)dr["Url"];
            if (dr["AllowComments"] != Convert.DBNull)
                post.AllowComments = (bool)dr["AllowComments"];
            if (dr["IsDraft"] != Convert.DBNull)
                post.IsDraft = (bool)dr["IsDraft"];
            //update the post image urls and post url
            post.Summary = ImageController.ResolveImagesForDisplay(post.Summary);
            post.Url = WebRoots.GetWebRoot + GenerateUrl(post);
            if (CloseDataReader)
                dr.Close();
            return post;
        }

        private static List<ArchiveData> FillArchives(IDataReader dr)
        {
            List<ArchiveData> list = new List<ArchiveData>();
            while (dr.Read())
            {
                list.Add(ActualReadArchive(dr));
            }
            dr.Close();
            return list;
        }
        private static ArchiveData ActualReadArchive(IDataReader dr)
        {
            ArchiveData archive = new ArchiveData();
            if (dr["YearNumber"] != Convert.DBNull)
                archive.Year = (int)dr["YearNumber"];
            if (dr["MonthNumber"] != Convert.DBNull)
                archive.Month = (int)dr["MonthNumber"];
            if (dr["MonthName"] != Convert.DBNull)
                archive.MonthName = (string)dr["MonthName"];
            return archive;
        }
        #endregion
    }
}
